package com.thunder.kocho.loki.builder;

import android.animation.Keyframe;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.graphics.Interpolator;
import android.util.Log;
import com.thunder.kocho.loki.Loki;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;

public class KeyFrameAnimatorBuilder {

    private static final String TAG="KeyFrameAnimatorBuilder";

    private final LinkedList<FrameData<?>> frameDatas;
    private Loki targetLoki;
    private int count;
    private TimeInterpolator interpolator;
    private int mode;
    private int duration;

    private enum AnimatorProperty{
        Alpha("Alpha"),
        Scale("Scale"),
        TranslateX("TranslateX"),
        TranslateY("TranslateY"),
        TranslatePercentageX("TranslatePercentageX"),
        TranslatePercentageY("TranslatePercentageY"),
        Rotate("Rotate"),
        RotateX("RotateX"),
        RotateY("RotateY"),
        Custom("");

        interface ICustomReflect{
            String getCustomReflect();
        }

        private ICustomReflect iCustomReflect;
        private final String reflect;

        AnimatorProperty(String reflect){
            this.reflect=reflect;
        }

        public void setCustomReflect(@NotNull ICustomReflect iCustomReflect){
            this.iCustomReflect=iCustomReflect;
        }

        public String getReflect(){
            if(!reflect.equals("")){
                return reflect;
            }else{
                if(iCustomReflect!=null){
                    return iCustomReflect.getCustomReflect();
                }else{
                    return null;
                }
            }
        }

    }

    private static class FrameData<T> {

        protected float[] fractions;
        protected AnimatorProperty animatorProperty;
        protected T[] values;
        protected TimeInterpolator interpolator=null;

        public FrameData(AnimatorProperty animatorProperty,float[] fractions, T[] values) {
            this(animatorProperty,fractions,values,null);
        }

        public FrameData(AnimatorProperty animatorProperty,float[] fractions, T[] values,TimeInterpolator interpolator) {
            this.animatorProperty=animatorProperty;
            this.fractions = fractions;
            this.values = values;
            if(interpolator!=null) {
                this.interpolator=interpolator;
            }
        }

    }

    private static class IntFrameData extends FrameData<Integer>{

        public IntFrameData(AnimatorProperty animatorProperty,float[] fractions,Integer[] values){
            super(animatorProperty,fractions,values);
        }

        public IntFrameData(AnimatorProperty animatorProperty,float[] fractions,Integer[] values,TimeInterpolator interpolator){
            super(animatorProperty,fractions,values,interpolator);

        }

    }

    private static class FloatFrameData extends FrameData<Float>{

        public FloatFrameData(AnimatorProperty animatorProperty,float[] fractions,Float[] values){
            super(animatorProperty,fractions,values);
        }

        public FloatFrameData(AnimatorProperty animatorProperty,float[] fractions,Float[] values,TimeInterpolator interpolator){
            super(animatorProperty,fractions,values,interpolator);
        }

    }

    public KeyFrameAnimatorBuilder(){
        frameDatas=new LinkedList<>();
        count=0;
        duration=0;
        mode=ValueAnimator.RESTART;
    }

    public KeyFrameAnimatorBuilder target(@NotNull Loki loki){
        this.targetLoki=loki;
        return this;
    }

    public KeyFrameAnimatorBuilder count(int count){
        if(count>=-1){
            this.count=count;
        }
        return this;
    }

    public KeyFrameAnimatorBuilder mode(int mode){
        if(mode==ValueAnimator.RESTART||mode==ValueAnimator.REVERSE){
            this.mode=mode;
        }
        return this;
    }

    public KeyFrameAnimatorBuilder interpolator(@NotNull TimeInterpolator interpolator){
        this.interpolator=interpolator;
        return this;
    }

    public KeyFrameAnimatorBuilder duration(int duration){
        this.duration=duration;
        return this;
    }

    public KeyFrameAnimatorBuilder alpha(float[] fractions,Integer... alphas){
        holder(AnimatorProperty.Alpha,fractions,alphas);
        return this;
    }


    public KeyFrameAnimatorBuilder scale(float[] fractions,Float... scales){
        holder(AnimatorProperty.Scale,fractions,scales);
        return this;
    }

    public KeyFrameAnimatorBuilder scale(float[] fractions, TimeInterpolator interpolator, Float... scales){
        holder(AnimatorProperty.Scale,fractions,scales,interpolator);
        return this;
    }

    public KeyFrameAnimatorBuilder rotate(float[] fractions,Integer...rotates){
        holder(AnimatorProperty.Rotate,fractions,rotates);
        return this;
    }

    public KeyFrameAnimatorBuilder rotate(float[] fractions,TimeInterpolator interpolator,Integer...rotates){
        holder(AnimatorProperty.Rotate,fractions,rotates,interpolator);
        return this;
    }

    public KeyFrameAnimatorBuilder rotateX(float[] fractions,Integer...rotateXs){
        holder(AnimatorProperty.RotateX,fractions,rotateXs);
        return this;
    }

    public KeyFrameAnimatorBuilder rotateX(float[] fractions,TimeInterpolator interpolator,Integer...rotateXs){
        holder(AnimatorProperty.RotateX,fractions,rotateXs,interpolator);
        return this;
    }

    public KeyFrameAnimatorBuilder rotateY(float[] fractions,Integer...rotateYs){
        holder(AnimatorProperty.RotateY,fractions,rotateYs);
        return this;
    }

    public KeyFrameAnimatorBuilder rotateY(float[] fractions,TimeInterpolator interpolator,Integer...rotateYs){
        holder(AnimatorProperty.RotateY,fractions,rotateYs,interpolator);
        return this;
    }

    public KeyFrameAnimatorBuilder translateX(float[] fractions,Integer...translateXs){
        holder(AnimatorProperty.TranslateX,fractions,translateXs);
        return this;
    }

    public KeyFrameAnimatorBuilder translateX(float[] fractions,TimeInterpolator interpolator,Integer...translateXs){
        holder(AnimatorProperty.TranslateX,fractions,translateXs,interpolator);
        return this;
    }

    public KeyFrameAnimatorBuilder translateY(float[] fractions,Integer...translateYs){
        holder(AnimatorProperty.TranslateY,fractions,translateYs);
        return this;
    }

    public KeyFrameAnimatorBuilder translateY(float[] fractions,TimeInterpolator interpolator,Integer...translateYs){
        holder(AnimatorProperty.TranslateY,fractions,translateYs,interpolator);
        return this;
    }

    public KeyFrameAnimatorBuilder translatePercentageX(float[] fractions,Float...translatePercentageX){
        holder(AnimatorProperty.TranslatePercentageX,fractions,translatePercentageX);
        return this;
    }

    public KeyFrameAnimatorBuilder translatePercentageX(float[] fractions,TimeInterpolator interpolator,Float...translatePercentageX){
        holder(AnimatorProperty.TranslatePercentageX,fractions,translatePercentageX,interpolator);
        return this;
    }

    public KeyFrameAnimatorBuilder translatePercentageY(float[] fractions,Float...translatePercentageY){
        holder(AnimatorProperty.TranslatePercentageY,fractions,translatePercentageY);
        return this;
    }

    public KeyFrameAnimatorBuilder translatePercentageY(float[] fractions,TimeInterpolator interpolator,Float...translatePercentageY){
        holder(AnimatorProperty.TranslatePercentageY,fractions,translatePercentageY,interpolator);
        return this;
    }

    public KeyFrameAnimatorBuilder custom(String reflect,float[] fractions,Float...customs){
        AnimatorProperty.Custom.setCustomReflect(() -> reflect);
        holder(AnimatorProperty.Custom,fractions,customs);
        return this;
    }

    public KeyFrameAnimatorBuilder custom(String reflect,float[] fractions,TimeInterpolator interpolator,Float...customs){
        AnimatorProperty.Custom.setCustomReflect(() -> reflect);
        holder(AnimatorProperty.Custom,fractions,customs,interpolator);
        return this;
    }

    public KeyFrameAnimatorBuilder custom(String reflect,float[] fractions,Integer...customs){
        AnimatorProperty.Custom.setCustomReflect(() -> reflect);
        holder(AnimatorProperty.Custom,fractions,customs);
        return this;
    }

    public KeyFrameAnimatorBuilder custom(String reflect,float[] fractions,TimeInterpolator interpolator,Integer...customs){
        AnimatorProperty.Custom.setCustomReflect(() -> reflect);
        holder(AnimatorProperty.Custom,fractions,customs,interpolator);
        return this;
    }

    private void holder(AnimatorProperty animatorProperty,float[] fractions,Integer[] values){
        if(fractions.length!=values.length){
            Log.w(TAG, "holder: the length of fractions and values does not match!" );
            return;
        }
        frameDatas.add(new IntFrameData(animatorProperty, fractions, values));
    }

    private void holder(AnimatorProperty animatorProperty,float[] fractions,Integer[] values,TimeInterpolator interpolator){
        if(fractions.length!=values.length){
            Log.w(TAG, "holder: the length of fractions and values does not match!" );
            return;
        }
        frameDatas.add(new IntFrameData(animatorProperty, fractions, values,interpolator));
    }

    private void holder(AnimatorProperty animatorProperty,float[] fractions,Float[] values){
        if(fractions.length!=values.length){
            Log.w(TAG, "holder: the length of fractions and values does not match!" );
            return;
        }
        frameDatas.add(new FloatFrameData(animatorProperty, fractions, values));
    }

    private void holder(AnimatorProperty animatorProperty,float[] fractions,Float[] values,TimeInterpolator interpolator){
        if(fractions.length!=values.length){
            Log.w(TAG, "holder: the length of fractions and values does not match!" );
            return;
        }
        frameDatas.add(new FloatFrameData(animatorProperty, fractions, values,interpolator));
    }

    public ObjectAnimator build(){
        if(frameDatas.size()==0||targetLoki==null){
            return null;
        }
        Iterator<FrameData<?>> iterator=frameDatas.iterator();
        ArrayList<PropertyValuesHolder> holders=new ArrayList<>();
        while(iterator.hasNext()){
            ArrayList<Keyframe> keyframes=new ArrayList<>();
            FrameData<?> frameData=iterator.next();
            if(frameData instanceof IntFrameData){
                for(int i=0;i<frameData.fractions.length;i++){
                    Keyframe keyframe=Keyframe.ofInt(frameData.fractions[i],(Integer)frameData.values[i]);
                    keyframes.add(keyframe);
                }
            }else{
                for(int i=0;i<frameData.fractions.length;i++){
                    Keyframe keyframe=Keyframe.ofFloat(frameData.fractions[i],(Float)frameData.values[i]);
                    keyframes.add(keyframe);
                }
            }
            if(frameData.interpolator!=null){
                for(int i=0;i<keyframes.size()-1;i++){
                    keyframes.get(i).setInterpolator(frameData.interpolator);
                }
            }
            String property=frameData.animatorProperty.getReflect();
            Keyframe[] keyframeArray=new Keyframe[keyframes.size()];
            PropertyValuesHolder holder=PropertyValuesHolder.ofKeyframe(property,keyframes.toArray(keyframeArray));
            holders.add(holder);
        }
        PropertyValuesHolder[] holderArray=new PropertyValuesHolder[holders.size()];
        ObjectAnimator animator=ObjectAnimator.ofPropertyValuesHolder(targetLoki,holders.toArray(holderArray));
        if(count!=0){
            animator.setRepeatMode(mode);
        }
        animator.setRepeatCount(count);
        if(interpolator!=null){
            animator.setInterpolator(interpolator);
        }
        if(duration!=0){
            animator.setDuration(duration);
        }
        return animator;
    }


}
